package com.wulong.office.util;

import com.wulong.office.bean.WordComment;
import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.xwpf.usermodel.XWPFComment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.xmlbeans.XmlObject;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.File;
import java.io.IOException;
import java.util.*;

public class Word2003CommentParseUtil {
    /** Word document */
    private XWPFDocument document;
    /** 批注所引用正文装配Map完毕标识 */
    private final String COMMENT_REF_FILLED_OK = "OK";
    /** 批注最大下标 */
    private String maxCommentIndex;
    /** 批注引用正文map,结构-<批注Id,WordComment> */
    private Map<String, WordComment> commentMap = new LinkedHashMap<>();

    /*
     * @param filePath Word文件路径
     */
    public Word2003CommentParseUtil(String filePath) {
        try {
            File file = new File(filePath);
            document = new XWPFDocument(POIXMLDocument.openPackage(file
                    .getCanonicalPath()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*
     * @param filePath Word文件路径
     */
    public Word2003CommentParseUtil(File file) {
        try {
            document = new XWPFDocument(POIXMLDocument.openPackage(file
                    .getCanonicalPath()));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public List<WordComment> parse(){
        List<WordComment> wordCommentList = new ArrayList<>();
        if(document!=null){
            List<XWPFComment> commentList = document.getDocComments().getComments();
            maxCommentIndex = String.valueOf(commentList.size() - 1);
            commentList.forEach(comment -> {
                WordComment wordComment = new WordComment();
                Calendar authDate = comment.getDate();
                authDate.add(Calendar.HOUR_OF_DAY, -8);//时区不正确
                StringBuilder paraId = new StringBuilder();
                comment.getParagraphs().forEach(xwpfParagraph -> {
                    Node node = xwpfParagraph.getCTP().getDomNode();
                    paraId.setLength(0);//一个批注可能有多个段落，默认id取最后一个段落id
                    paraId.append(",").append(getAttribute(node, "w14:paraId"));
                });
                wordComment.setId(comment.getId())
                        .setComment(comment.getText().replaceAll("\n", "<br>"))
                        .setAuthor(comment.getAuthor())
                        .setDate(authDate.getTime())
                        .setParaId(paraId.substring(1));
                commentMap.put(comment.getId(), wordComment);
            });
            Iterator<POIXMLDocumentPart> iter = document.getRelations().iterator();
            while (iter.hasNext()) {
                try {
                    POIXMLDocumentPart p = iter.next();
                    if (p.getPackagePart().getPartName().toString().matches("/word/commentsExtended.xml")) {
                        XmlObject xmlObject = XmlObject.Factory
                                .parse(p.getPackagePart().getInputStream());
                        parseCommentsExtended(xmlObject.getDomNode());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            fillCommentRef(document.getDocument().getDomNode(), null, null);
            commentMap.forEach((id, wordComment) -> {
                wordCommentList.add(wordComment);
            });
        }
        return wordCommentList;
    }

    /**
     * 解析注释上下级关系
     * @param node
     */
    private void parseCommentsExtended(Node node){
        if (node == null) {
            throw new IllegalArgumentException("node不能为空");
        }
        if ("w15:commentEx".equals(node.getNodeName())) {
            String paraId = getAttribute(node, "w15:paraId");
            String paraIdParent = getAttribute(node, "w15:paraIdParent");
            if(paraIdParent!=null){
                for(WordComment wordComment : commentMap.values()){
                    if(wordComment.getParaId().contains(paraId)){
                        wordComment.setParaIdParent(paraIdParent);
                        break;
                    }
                }
            }
        }
        if (node.hasChildNodes()) {
            NodeList temp = node.getChildNodes();
            for (int i = 0; i < temp.getLength(); i++) {
                parseCommentsExtended(temp.item(i));
            }
        }
    }

    /*
     * 组装批注引用文本
     * @param node WordProcessingML node
     * @param map <commentId,text>，用于存储每个commentRange包裹的正文
     * @param convertOK 正文组装完毕标识 ,组装完毕 = "OK"
     */
    private void fillCommentRef(Node node, Map<String, StringBuilder> map, String convertOK) {
        if (node == null) {
            throw new IllegalArgumentException("node不能为空");
        }
        if (map == null) {
            map = new HashMap<>();
        }
        if ("w:t".equals(node.getNodeName()) && !map.isEmpty()) {
            map.forEach((k,v) -> {
                v.append(node.getFirstChild().getNodeValue());
            });
        } else if ("wp:docPr".equals(node.getNodeName()) && !map.isEmpty()) {
            map.forEach((k,v) -> {
                v.append("[").append(getAttribute(node, "name")).append("]");
            });
        } else if ("w:commentRangeStart".equals(node.getNodeName())) {
            map.put(getAttribute(node, "w:id"), new StringBuilder());
        } else if ("w:commentRangeEnd".equals(node.getNodeName()) && !map.isEmpty()) {
            String id = getAttribute(node, "w:id");
            WordComment comment = commentMap.get(id);
            comment.setCommentRef(map.remove(id).toString());
            if (id.equals(maxCommentIndex)) {
                convertOK = COMMENT_REF_FILLED_OK;
            }
        }
        if (node.hasChildNodes()) {
            NodeList temp = node.getChildNodes();
            for (int i = 0; i < temp.getLength(); i++) {
                if (COMMENT_REF_FILLED_OK.equals(convertOK)) {
                    break;
                }
                fillCommentRef(temp.item(i), map, convertOK);
            }
        }
    }

    /***
     * @param node
     *            当前的Node
     * @param attName
     *            要获取的属性名
     * @return 属性值,没有该属性时返回null
     */
    private String getAttribute(Node node, String attName) {
        return (node.hasAttributes() && node.getAttributes().getNamedItem(
                attName) != null) ? node.getAttributes().getNamedItem(attName)
                .getNodeValue() : null;
    }

    public static void main(String[] args) {
        String filePath = "D:/新建 Microsoft Word Document.docx";
        Word2003CommentParseUtil wordParseUtil = new Word2003CommentParseUtil(filePath);
        List<WordComment> wordCommentList = wordParseUtil.parse();
        wordCommentList.forEach(wordComment -> {
            System.out.println(wordComment);
        });
    }
}
